#include "glViewer.h"
#include "glAux.h"
#include <QMouseEvent>
#include <windows.h>
#include <gl/GLU.h>
#include <qmath.h>

GLViewer::GLViewer(QWidget *parent /* = 0 */)
	:QGLWidget(parent)
	, draw_points_(false)
	, draw_edges_(false)
	, draw_triangles_(true)
	, left_button_down_(false)
	, right_button_down_(false)
	, gl_size_width(0)
	, gl_size_height(0)
	, eye_dist_scale_(0.4)
{
	light_pos_ = Eigen::Vector3d(0.0, 0.0, 1.0);
	control_ball_ = (new CArcBall);
	control_ball_->InitBall();
}

GLViewer::~GLViewer()
{
	if (control_ball_ != NULL)
	{
		delete control_ball_;
		control_ball_ = NULL;
	}
}

void GLViewer::mousePressEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton)
	{
		prev_mouse_down_point_ = QPoint(event->x(), event->y());

		control_ball_->MouseDown(PixelPoint(event->x(), event->y()), gl_size_width, gl_size_height);

		if (event->button() == Qt::LeftButton) {
			left_button_down_ = true;
		}
		if (event->button() == Qt::RightButton) {
			right_button_down_ = true;
		}
	}
}

void GLViewer::mouseReleaseEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton)
	{
		control_ball_->MouseUp(PixelPoint(event->x(), event->y()), gl_size_width, gl_size_height);
		if (event->button() == Qt::LeftButton) {
			left_button_down_ = false;
		}
		if (event->button() == Qt::RightButton) {
			right_button_down_ = false;
		}
	}
}

void GLViewer::mouseMoveEvent(QMouseEvent *event)
{
	if (!left_button_down_ && !right_button_down_)
	{
		return;
	}

	if (left_button_down_)
	{
		control_ball_->MouseMove(PixelPoint(event->x(), event->y()), gl_size_width, gl_size_height);
	}
	else if (right_button_down_)
	{
		float scale = 2 * (event->y() - prev_mouse_down_point_.y()) / (float)gl_size_width;
		eye_dist_scale_ += scale;
		if (eye_dist_scale_ < .01f) eye_dist_scale_ = 0.01f;
		if (eye_dist_scale_ > 10.f) eye_dist_scale_ = 10.f;

		prev_mouse_down_point_ = QPoint(event->x(), event->y());
	}

	glPushMatrix();
	setRenderingView();
	glMultMatrixf(control_ball_->GetBallMatrix());
	updateGL();
}

void GLViewer::wheelEvent(QWheelEvent *event)
{
	float zDelta = ((float)event->delta()) / 25.0;
	float scale = (4*zDelta) / gl_size_width;
	eye_dist_scale_ += scale;
	if (eye_dist_scale_ < .01f) eye_dist_scale_ = 0.01f;
	if (eye_dist_scale_ > 10.f) eye_dist_scale_ = 10.f;

	glPushMatrix();
	setRenderingView();
	glMultMatrixf(control_ball_->GetBallMatrix());
	updateGL();
}

void GLViewer::initializeGL()
{
	// OpenGL state
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glDisable(GL_DITHER);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_DOUBLEBUFFER);
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_LINE_SMOOTH);

	// add
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
	glEnable(GL_COLOR_MATERIAL);

	control_ball_->InitBall();
}

void GLViewer::resizeGL(int _w, int _h)
{
	gl_size_width = _w;
	gl_size_height = _h;
	
	glViewport(0, 0, gl_size_width, gl_size_height);
	glMatrixMode(GL_PROJECTION);
	setRenderingView();
	updateGL();
}

void GLViewer::setRenderingView()
{
	glViewport(0, 0, gl_size_width, gl_size_height);

	glMatrixMode(GL_PROJECTION);

	Eigen::Vector3d eye_pos(0.f, 0.f, 10.39f*eye_dist_scale_);

	glLoadIdentity();
	{
		double theta = 2 * atan2(1, tan(3 * M_PI / 8));

		gluPerspective(theta * 180 / M_PI, (float)gl_size_width / (float)gl_size_height,
			0.01039*eye_dist_scale_, 20.78*eye_dist_scale_);
	}

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	gluLookAt(eye_pos.x(), eye_pos.y(), eye_pos.z(), 0, 0, 0, 0.f, 1.f, 0.f);
}

void GLViewer::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	if (mesh_.m_nVertices > 0)
	{
		draw();
	}
}

void GLViewer::unifyMesh()
{
	double minx = 0,  miny = 0, minz = 0;
	double maxx = 0, maxy = 0, maxz = 0;

	Enriched_Model::Vertex_iterator pVertex = mesh_.getMesh()->vertices_begin();
	minx = maxx = pVertex->point().x();
	miny = maxy = pVertex->point().y();
	minz = maxz = pVertex->point().z();

	for (++pVertex; pVertex != mesh_.getMesh()->vertices_end(); ++pVertex)
	{
		const Enriched_Model::Point& p = pVertex->point();

		minx = __min(minx, p.x());
		miny = __min(miny, p.y());
		minz = __min(minz, p.z());

		maxx = __max(maxx, p.x());
		maxy = __max(maxy, p.y());
		maxz = __max(maxz, p.z());
	}

	Eigen::Vector3d minp(minx, miny, minz), maxp(maxx, maxy, maxz);
	Eigen::Vector3d center = (minp + maxp)*0.5;

	Enriched_Model::Point _center(center.x(), center.y(), center.z());	//calculate the current center 

																		//Calculate scale ration
	double fScale = (maxp-minp).norm();
	fScale = 2 / fScale;

	for (pVertex = mesh_.getMesh()->vertices_begin();pVertex != mesh_.getMesh()->vertices_end(); pVertex++)
	{
		Enriched_Model::Point& p = pVertex->point();
		p = CGAL::ORIGIN + ((p - _center)*fScale);
	}
}

void GLViewer::draw()
{
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glEnable(GL_COLOR_MATERIAL);
	if (draw_triangles_)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glColor3f(0.67f, 0.67f, 0.67f);
		for (Enriched_Model::Face_iterator f_it = mesh_.getMesh()->facets_begin();
			f_it != mesh_.getMesh()->facets_end(); f_it++)
		{
			const Enriched_Model::Facet::Normal_3& normal = f_it->normal();
			glNormal3f(normal[0], normal[1], normal[2]);
			glBegin(GL_POLYGON);
			Enriched_Model::Halfedge_around_facet_circulator pHalfedge = f_it->facet_begin();
			do
			{
				const Enriched_Model::Point& point = pHalfedge->vertex()->point();
				glVertex3f(point[0], point[1], point[2]);
			} while (++pHalfedge != f_it->facet_begin());

			glEnd();
		}
		glPopAttrib();
	}
	if (draw_edges_)
	{
		// Default color
		glColor3f(0.2f, 0.3f, 0.5f);
		glLineWidth(.5f);
		glBegin(GL_LINES);

		for (Enriched_Model::Halfedge_iterator pEdge = mesh_.getMesh()->edges_begin();
			pEdge != mesh_.getMesh()->edges_end();
			pEdge++)
		{
			Enriched_Model::Point& v0 = pEdge->vertex()->point();
			Enriched_Model::Point& v1 = pEdge->opposite()->vertex()->point();

			const Enriched_Model::Facet::Normal_3& n0 = pEdge->vertex()->normal();
			const Enriched_Model::Facet::Normal_3& n1 = pEdge->opposite()->vertex()->normal();

			glNormal3f(n0.x(), n0.y(), n0.z());
			glVertex3f(v0.x(), v0.y(), v0.z());

			glNormal3f(n1.x(), n1.y(), n1.z());
			glVertex3f(v1.x(), v1.y(), v1.z());
		}

		glEnd();
		glPopAttrib();
	}
	if (draw_points_)
	{
		glColor3f(1.0f, 0.0f, 0.0f);
		glPointSize(1.2f);
		glBegin(GL_POINTS);
		for (Enriched_Model::Vertex_iterator v_it = mesh_.getMesh()->vertices_begin();
			v_it != mesh_.getMesh()->vertices_end(); ++v_it)
		{
			const Enriched_Model::Vertex::Normal_3& normal = v_it->normal();
			glNormal3f(normal[0], normal[1], normal[2]);
			glVertex3f(v_it->point().x(), v_it->point().y(), v_it->point().z());
		}
		glEnd();
	}
	glPopAttrib();
}